package com.dbflow5.observing;

import java.util.Arrays;

/**
 * Description: Keeps track of how many observers are registered on a [TableObserver] at a given time.
 */
public class ObservingTableTracker{

    public ObservingTableTracker(int tableCount){
        observerPerTable = new Long[tableCount];
        previousTriggerStates = new Boolean[tableCount];
        triggerStateChanges = new Operation[tableCount];
        Arrays.fill(observerPerTable, 0L);
        Arrays.fill(previousTriggerStates, false);
        Arrays.fill(triggerStateChanges, Operation.None);
    }

    enum Operation {
        None,
        Add,
        Remove
    }

    private final Long[] observerPerTable;
    private final Boolean[] previousTriggerStates;

    // caches the changes in a fixed array.
    private final Operation[] triggerStateChanges;

    private boolean needsSync = false;
    private boolean pendingSync = false;


    boolean onAdded(int[] tableIds){
        return adjustObserverCount(tableIds, 1L, 0L);
    }

    boolean onRemoved(int[] tableIds){
        return adjustObserverCount(tableIds, -1L, 1L);
    }

    private boolean adjustObserverCount(int[] tableIds, long value, long countToSync) {
        boolean syncTriggers = false;
        synchronized(this) {
            for(int tableId : tableIds){
                Long count = observerPerTable[tableId];
                observerPerTable[tableId] = count + value;
                if (count == countToSync) {
                    syncTriggers = true;
                    needsSync = true;
                }
            }
        }
        return syncTriggers;
    }

    void syncCompleted() {
        synchronized(this) {
            pendingSync = false;
        }
    }

    public Operation[] tablesToSync(){
        if (!needsSync || pendingSync) {
            return null;
        }

        for (int index=0;index<observerPerTable.length;index++){
            long observerCount = observerPerTable[index];
            boolean hasObservers = observerCount > 0;
            if (hasObservers != previousTriggerStates[index]) {
                if (hasObservers) {
                    triggerStateChanges[index] = Operation.Add;
                } else {
                    triggerStateChanges[index] = Operation.None;
                }
            } else {
                triggerStateChanges[index] = Operation.None;
            }
            previousTriggerStates[index] = hasObservers;
        }

        pendingSync = true;
        needsSync = false;
        return triggerStateChanges;
    }
}